#include <iostream>
#include <unistd.h>
#include <string>
#include <assert.h>
#include <vector>
#include <time.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/types.h>
using namespace std;
#define NUM_Process 5
//生成随机数
#define MakeSeed() srand((unsigned long)time(nullptr) ^ getpid() ^ 0x171237 ^ rand() % 1234)

//函数指针
typedef void(*func)();
void Func1()
{
    cout<<getpid()<<" :call Func1"<<endl;
    sleep(1);
}

void Func2()
{
    cout<<getpid()<<" :call Func2"<<endl;
    sleep(1);
    
} 

void Func3()
{
    cout<<getpid()<<" : call Func3"<<endl;
    sleep(1);
    
} 
void  LoadFunc(vector<func> *subFunc)  //输出型参数
{
    assert(subFunc);
    subFunc->push_back(&Func1);
    subFunc->push_back(&Func2);
    subFunc->push_back(&Func3);
}

//键值对{namex : func1}
class info_Process{
public:
     info_Process(pid_t pid,int id)   //子进程ID和管道id（也就是文件描述符）
     :pid_(pid),id_(id)
     {
        char namebuffer[1024];
        snprintf(namebuffer,sizeof namebuffer,"Process%d[%d][%d]",cnt,pid_,id);
        name_ = namebuffer;
     }
public:
    string name_; //每个子进程的名字
    int id_;      //子进程读取管道的id
    pid_t pid_;   //子进程的PID
    static int cnt;
};
int info_Process::cnt=0;



int recvTask(int fds)
{
    int code=0;
    ssize_t n = read(fds,&code,sizeof code);
    if(n==4) return code;
    else if(n<=0) return -1;
    else return 0;
}

void creatSubPro(vector<info_Process> *subPro,vector<func> &subFunc)   //做输出型参数
{
  for(int i=0 ; i<NUM_Process ; ++i)
    {
        int fds[2];
        ssize_t n= pipe(fds);  //成功了返回0
        assert(n == 0);
        (void)n;
        pid_t id=fork();
        if(id==0)
        {
            //子进程关闭write功能，只留下了读的功能
            close(fds[1]);
            while(true)
            {
                //1.获取命令码，如果没有发送，需要阻塞
                int commandCode = recvTask(fds[0]);
                //2.完成命令
                if(commandCode >=0 && commandCode < subFunc.size())
                {
                    subFunc[commandCode]();       
                }
                else if(commandCode == -1) break;
            }
            //子进程退出
            exit(0);
        }
        //将子进程的id信息，名字，文件口的id存储进去
        //创建一个临时的info_Process变量，存储每个进程的参数
        close(fds[0]);
        info_Process temp_info(id,fds[1]);
        subPro->push_back(move(temp_info));  //move这里把temp_info变成左值，直接记录进subPro
    }
}

void SendTask(const info_Process& subProcess,const int index)
{
    cout<<"send task num: "<<index<<"send to ->"<<subProcess.name_<<endl;
    int n=write(subProcess.id_,&index,sizeof(index));
    assert(n==sizeof(int));
    (void)n; //防止出现warning警告

}

void LoadBalanceFactor(const vector<info_Process> &subPro,const vector<func> &subFunc,int num)
{
    //获取到子进程的总量以及任务的总量
    int processnum=subPro.size();
    int tasknum = subFunc.size();
    bool forever = (num==0?true:false);
    while(true)
    {
        //1.那个子进程去执行任务
        int IndexProcess = rand()%processnum;
        //2.规定什么任务
        int IndexTask = rand()%tasknum;
        //3.分发任务
        SendTask(subPro[IndexProcess],IndexTask);
        sleep(1);
        if(!forever)
        {
            num--;
            if(num==0) break;   //如果任务数量为0的话退出进程
        }
    }
    //当退出while循环的时候，这里需要对子进程进行结束和等待
    //防止出现僵尸进程
    //这里关闭的时候应该注意：在创建的时候是循环创建的，所以第2个子进程会继承
    //所有的文件描述符，也就是说 假设 3-->进程1   子进程2的文件描述符中有一个文件描述符3也指向进程1
    //也就是约到后面的进程，就会继承父进程的所有的文件描述符去指向上面的子进程
    //所以在关闭子进程的时候，应该注意如果是waitpid的方式，需要从末尾开始关闭，如果从头开始关闭的话，是不能完全关闭的
    //
    // write 被关闭的时候，read方也会被关闭
    for(int i=0;i<subPro.size();++i)
    {
        close(subPro[i].id_);
    }


}
void waitProcess(vector<info_Process> processes)
{
    int processnum = processes.size();
    for(int i = 0; i < processnum; i++)
    {
        waitpid(processes[i].pid_, nullptr, 0);
        std::cout << "wait sub process success ...: " << processes[i].pid_ << std::endl;
    }
}
int main()
{
    MakeSeed();
    //1.首先创建子进程的Vector 以及 任务的vector
    vector<info_Process> subPro;
    vector<func> subFunc;
    //将所有的函数任务放入到subFunc中去
    LoadFunc(&subFunc);
    creatSubPro(&subPro,subFunc);

   //父进程
    int task_num=10;
    //父进程负责分发命令给子进程
    LoadBalanceFactor(subPro,subFunc,task_num);

    waitProcess(subPro);
    return 0;
}